/**
* @file
* @brief This file handles all server related functions.
*/

/*
 For now, jump to processconfigline() below and read Jerry's Task 1
 Then jump to file database.h and read Jerry's Task 2
 Note: This does not mean u have 2 different tasks, you have the same only one current task, implement processconfigline() properly
 but the numbers are just to guide you so you know the order you have to read from what I am telling u to do.

Note: I may be wrong, ask me if you feel something should be change with my methods
 and dont just implement what i tell you, understand why and let me know if you dont get what I am saying
 cause when i code stuff that depends on what you're doing, i'll assume that you'll implement exactly as i told you too
Note: Don't delete any of my comments for you, once you're done, I'll need to refer to them
 to make sure you accounted for all the cases i told you about, i'll delete these comments as I confirm they are implemented
Jerry Task 1: server.c
Jerry Task 2: database.h
Jerry Task 3: utils.c
@note: These are not 3 tasks, it is just one taks which is implement processing the new configuration file.
*/


//-------------------------------------------------------
// Header Files
//-------------------------------------------------------
#include "server.h" // for client structure
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define MAX_LEN 80 //Maximum length of message
#include <unistd.h> // for getopt
#include <limits.h> // for PATH_MAX
#include <ctype.h> // for isprint()
// Flex/ Bison parsing occurs in utils.c where it includes the proper header files compiled properly in Makefile

//-------------------------------------------------------
// Constants
//-------------------------------------------------------
#define MAX_LISTENQUEUELEN 20   // The maximum number of queued connections.
#define LOGGING 1
    ///< 0 -> disabled logging,
    ///< 1 -> log to stdout,
    ///< 2 -> log to file, that follows format: Client/Server-yyyy-mm-dd-hh-ss.log
//-------------------------------------------------------
// Function Prototypes

process (char *deckfname, char *msg, char * newmsg,  char mode); 

//-------------------------------------------------------
// Main
//-------------------------------------------------------
/**
* Main function that parses in commandline arguments and initializes the server
* @param argv[1] configurationFile
*/

    pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
    int concurrency = 1;

int main(int argc, char *argv[])
{
/* TEMP FOR DEBUGGING
    addclient(20);
    printf("Top fd is %d\n", top->fd);
*/
/*
// These are in storage.c for M2, need to change for M4 when multi client
    int authen = 0; // single client server not authenticated
    int connected = 0; // single client server not connected
*/




    struct client *p, *nextp; // 2 pointers for the linked list
    struct client *p1, *p2; // for anything else to match up 
    pthread_t threads[20];
	int validThread[20] = {0};
    struct thread_data threaddata[20];
    int threadNum = 0;
    int threadErr = 0;
	int i = 0;

    int totalProcTime = 0;	// total Processing Time
    struct timeval start_time, end_time;	//Same as client.c implementation
    int tempT = 0;
    char timeOut[64];
    // Process command line arguments.
    // This program expects exactly one argument: the config file name.
    assert(argc > 0); // aborts program if this is false
    if (argc != 2)
    {
	printf("Usage %s <config_file>\n", argv[0]);
	exit(EXIT_FAILURE);
    }
    // Configuration file
    char *config_file = argv[1];
    // Read the config_file and store into the config_params struct
    struct config_params params; // structure to marshall and store configuration parameters into a structure
    struct check_duplmis check; // structure to check duplicates of configuration parameters
    initialCheck(&check);
    // @note Table is currently defined in server.c only
    // Initialize table as a sorted array
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////initializeSortedArrayTable();
    // @note CTRL+C automatically frees all memory according to T.A
    // Read the configuration file and check to make sure it meets the configuration file requirements

/*
Jerry's Task 1 Begin:
Modify read_config below that is in utils.h and defined in utils.c
such that it is able to accept the new table line structure

Look at Milestone 3 for examples
In short,
table format will be:
table nameOfTable col1Name:Type, col2Name:Type, ... , colXName:Type
// Note: X <= MAX_COLUMNS_PER_TABLE
// Note: Type can be a char[SIZE] which means when you create a name for this column,
// you need to create char name[SIZE] and make sure your input for later insertions does not exceed SIZE-1
Jerry's Task 1 END
*/
// THIS ENTIRE FUNC. HAS TO BE CHANGED TO CONFORM TO YACC & LEX
    int status = read_config(config_file, &params, &check); // returns 0 if successful
    if (status != 0)
    {
	printf("Error processing config file.\n");
	exit(EXIT_FAILURE);
    }
    // Sort the tables
    /////////////////////////////////////////////////////////////////////////sortTable(); // TODO: Change this to merge/quick sort instead of bubble sort
printf("STARTING TO PRINT TABLE NAME:\n");
    printTableName();
    // Update concurrency
    concurrency = params.concurrency; 

	if(concurrency != 0 && concurrency != 1)		// ONLY IMPLEMENT THESE CONCURRENCIES
		exit(EXIT_FAILURE);
		
printf("CONCURRENCY VALUE IN SERVER.C is %d\n", concurrency); 

	char deckfname[50] ="deck.txt"; 
//	char msg[50] = "DOABARRELROLL";  
//	char msg[50] = "LAKEHYLIA"; 
//	char msg[100] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";  
	char msg[100] = "ABCDEFG@HIJ,KLMN.OPQR STUV+WXYZabcdefghijklmnopqrstuvwxyz";  	 	 
	char newmsg[100] = "";
	char finalmsg[100] = "";  
	char mode = 'e';
printf("message to encrypt is %s\n", msg); 
 process (deckfname, msg, newmsg,  mode); 
//	strcpy(msg, "OXXIKQCPSZXWW"); 
printf("message to decrypt is %s\n", newmsg); 
	mode = 'd'; 
 process (deckfname, newmsg, finalmsg,  mode); 
printf("final message  is %s\n", finalmsg); 
    // Set up time for logging
    char fileName[MAX_STRTYPE_SIZE];
    time_t basetime;
    struct tm * conTime;
    basetime = time(NULL);
    conTime = localtime(&basetime);
    strftime(fileName, sizeof(fileName), "Server-%Y-%m-%d-%H-%M-%S.log",conTime);
    int logChoice = LOGGING;
    // Initialize fileServer based on LOGGING
    switch(logChoice)
    {
	case 0: // Disable Logging
	    fileServer = NULL;
	    break;
	case 1: // Log to stdout
	    fileServer = stdout;
	    break;
	case 2: // Log to File
	    // Append file for appending an update
	    fileServer = fopen (fileName,"a+");
	    if (fileServer == NULL)
	    {
		printf("Error opening file.\n");
		fclose(fileServer);
		exit(EXIT_FAILURE);
	    }
	    break;
    }
    // Server on localhost:1376
    sprintf(message, "Server on %s:%d\n", params.server_host, params.server_port);
    logger (fileServer, message);
    // Create a socket for the server
    // listensock is the sockfd to be listening to
    int listensock = socket(PF_INET, SOCK_STREAM, 0);
    if (listensock < 0)
    {
	printf("Error creating socket.\n");
	Fclose(logChoice, 1);
	exit(EXIT_FAILURE);
    }
    // Allow listening port to be reused if defunctional
    int yes = 1;
    status = setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);
    if (status != 0)
    {
	printf("Error configuring socket.\n");
	Fclose(logChoice, 1);
	exit(EXIT_FAILURE);
    }
    // Bind to the listening port.
    struct sockaddr_in listenaddr;
    memset(&listenaddr, 0, sizeof listenaddr);
    listenaddr.sin_family = AF_INET;
    listenaddr.sin_port = htons(params.server_port);
    inet_pton(AF_INET, params.server_host, &(listenaddr.sin_addr)); // bind to local IP address
    // Let server bind to this socket
    status = bind(listensock, (struct sockaddr*) &listenaddr, sizeof listenaddr);
    if (status != 0)
    {
	printf("Error binding socket.");
	Fclose(logChoice, 1);
	exit(EXIT_FAILURE);
    }
    // Start listening for connections.
    status = listen(listensock, MAX_LISTENQUEUELEN);
    if (status != 0)
    {
	printf("Error listening on socket.\n");
	Fclose(logChoice, 1);
	exit(EXIT_FAILURE);
    }
    // Listen loop.
    int wait_for_connections = 1;
	// Wait for a connection.
	struct sockaddr_in clientaddr;
	socklen_t clientaddrlen = sizeof clientaddr;
	int clientsock;
    // Infinite while loop for milestone 1


//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
	if(concurrency == 0)
	{
    while (wait_for_connections)
    {

	// Accept any calls from any client
	// @note: Will just poll till a client is accepted
	// @note: Right now only works with 1 clientsock
	 clientsock = accept(listensock, (struct sockaddr*)&clientaddr, &clientaddrlen);
	// Add this client to linked list of clients
	addclient(clientsock); 
	// Accepted client wil return it's file descriptor as clientsock
	if (clientsock < 0)
	{
	    printf("Error accepting a connection.");
	    Fclose(logChoice, 1);
	    exit(EXIT_FAILURE);
	}
	sprintf(message, "Got a connection from %s:%d.\n", inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port);
	logger(fileServer, message);
	    // TODO: For Milestone 3, change code to work with > 1 client
	    // Get commands from client.
	    int wait_for_commands = 1;
	    do
	    {
	        // Read a line from the client.
	        char cmd[MAX_CMD_LEN];
	        // Read from clientsock's file descriptor to command
	        int status = recvline(clientsock, cmd, MAX_CMD_LEN);
		if (status != 0)
		{
		    // Here, either an error occurred or the client closed the connection.
		    // Stop waiting for commands
		    wait_for_commands = 0;
		}
		else
		{
		    // Handle the command from the client.
		    // @note: For milestone1, this just prints it back out to server
		    //********** TESTING ONLY
		    //**********/
		    int status = handle_command(clientsock, cmd, &params);
		    // If handled command fails, stop waiting for command
		    if (status != 0)
		    {
		   	printf("Error: Handle command return status of -1 to main() in server");
			wait_for_commands = 0; // stop waiting for commands
			exit(EXIT_FAILURE); // @note original code for M1 by prof. was to just EXIT_SUCCESS at bottom
		    }
		}
	    }
	    while (wait_for_commands); //do while client is still interacting
	    // Close the connection with the client.
	    //close(clientsock);
	    sprintf(message, "Closed connection from %s:%d.\n", inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port);
	    logger(fileServer, message);
	} // end of while wait for connections   for M3 code  // Stop listening for connections.
	}	// If loop for if using threads
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
	else if (concurrency == 1)
	{
    while (wait_for_connections)
    {

	// Accept any calls from any client
	// @note: Will just poll till a client is accepted
	// @note: Right now only works with 1 clientsock
	 clientsock = accept(listensock, (struct sockaddr*)&clientaddr, &clientaddrlen);
	// Add this client to linked list of clients
	addclient(clientsock); 
	// Accepted client wil return it's file descriptor as clientsock
	if (clientsock < 0)
	{
	    printf("Error accepting a connection.");
	    Fclose(logChoice, 1);
	    exit(EXIT_FAILURE);
	}
	sprintf(message, "Got a connection from %s:%d.\n", inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port);
	logger(fileServer, message);
	    //Thread create here
	    for(i=0;i<MAX_CONNECTIONS;i++)
	    {
		// Get total number of current threads 
		if(validThread[i]==0)
		{
		    threadNum=i;
		    validThread[i]=1;
		    break;
		}
	    	if(i==MAX_CONNECTIONS-1)	// If last iteration, and not found, too many clients exit failure;
	    	{
		    sprintf(message, "Too many clients: error\n");
		    logger(fileServer, message);
		    exit(EXIT_FAILURE);
		}
	    }
	    threaddata[threadNum].params = &params;
	    threaddata[threadNum].clientaddr = clientaddr;
	    threaddata[threadNum].clientsock = clientsock;
	    threaddata[threadNum].threadNum = &validThread[i];
	    threadErr = pthread_create(&threads[threadNum],NULL,serverThreads, (void*) &threaddata[threadNum]);
	    threadNum++;
	    if(threadErr)
	    {
		sprintf(message, "ThreadError is %d\n", threadErr);
		logger(fileServer, message);
		exit(EXIT_FAILURE);
	    }
	} // END OF WHILE WAIT FOR CONNECtions   for threads  // Stop listening for connections.
	}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
	else if (concurrency == 2)
	{
	    while (wait_for_connections)
	    {
		printf("IN SELECT\n"); 
		fd_set fdlist;
		// Need to know maxfd for select()
		int maxfd = listensock;
		FD_ZERO(&fdlist); // clears the set
		FD_SET(listensock, &fdlist); // adds listenfd (host) to fdlist
		// continue moving as long p is not NULL 
		// TODO: MAKE IT LESS THAN MAX CONNECTION CLIENTS 
		// TODO: TRANSACTION STUFF 
		// TODO: WRITE TO FD AS WELL NOT JUST READ 
printf("HAHA1\n"); 
		for (p = top; p; p = p->nextpos) // NULL at end of linked list
		{
printf("HAHA2\n"); 
		    FD_SET(p->fd, &fdlist); // include everything into the fdlist
		    if (p->fd > maxfd)
			maxfd = p->fd; // update maxfd
printf("HAHA 3 maxfd is %d\n", maxfd); 
		}
printf("HAHA20\n"); 
		//===================================================
		// Select()
		//===================================================
		// don't write or do any exception, and no timeval. NEED CHANGE THIS TO DO WRITE 
		if ((select(maxfd + 1, &fdlist, NULL, NULL, NULL)) < 0) // returns -1 on error, 0 if timeout, and file descriptor
		{	// (numfd, readfd, writefd, exceptfd, timeval)
printf("HAHA4\n"); 
		    printf(" ERROR IN SELECT!!\n"); 
		    exit(EXIT_FAILURE);	
		}
printf("HAHA30\n"); 
		// No error occured, process select options
		// If listenfd has read, it means there is a new connection
		if (FD_ISSET(listensock, &fdlist)) // connect if new client is connecting
		{
printf("HAHA5\n"); 
		// If the host socket is still in the fdlist
		    newconnection(listensock);// accept connection & update linked list
		    // All Clients
printf("HAHA70\n"); 
		}
printf("HAHA40\n"); 
		for (p = top; p; p = nextp)
		{
printf("HAHA6\n"); 
		    nextp = p->nextpos; // in case we remove this client because of error
printf("HAHA61\n"); 
		    if (FD_ISSET(p->fd, &fdlist)) // if this client has buffer's to read
		    {
printf("HAHA62\n"); 
			// Read a line from the client.
			char cmd[MAX_CMD_LEN];
			int currentSock = p->fd; 
printf("HAHA63\n"); 
			// Read from clientsock's file descriptor to command
			int status = recvline(currentSock, cmd, MAX_CMD_LEN);
printf("HAHA64\n"); 
			if (status != 0)
			{
printf("HAHA7\n"); 
			    // Here, either an error occurred or the client closed the connection.
			    // Stop waiting for commands
			    // wait_for_commands = 0;
			    printf(" TODO: REMOVE CLIENT!!!!\n" ); 
				struct client *removeClient = getclient(currentSock); 
				close(removeClient->fd); 
				removeclient(removeClient);
				
			}
			else
			{
printf("HAHA8\n"); 
			    // Handle the command from the client.
			    // @note: For milestone1, this just prints it back out to server
			    //********** TESTING ONLY
			    //**********/
			    int status = handle_command(currentSock, cmd, &params);
printf("HAHA83\n"); 
			    // If handled command fails, stop waiting for command
			    if (status != 0)
			    {
				printf("Error: Handle command return status of -1 to main() in server");
				//	wait_for_commands = 0; // stop waiting for commands
				exit(EXIT_FAILURE); // @note original code for M1 by prof. was to just EXIT_SUCCESS at bottom
			    }
			}
		    }

		} // end of For
	    } // end of while(1) 
	} // end of concurrency = 2
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
	else if (concurrency == 3)
	{
	    printf("IN FORK, not implemented\n"); 
	    close(listensock);
    	    return EXIT_SUCCESS;
	}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
	else
	{
	    // Should never be here since lex and yacc fails it first
	    printf("ERROR, concurrency is not 0-3\n"); 
	    close(listensock);
    	    return EXIT_SUCCESS;
	}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
    close(listensock);
    return EXIT_SUCCESS;
}

//-------------------------------------------------------
// Helper Functions
//-------------------------------------------------------
/**
* This function handles a command given by sending it as a string to a socket given.
* @param sock The socket descriptor to output commands to
* @param cmd A string to be sent to a socket descriptor
* @returns 0 if success, returns error code if failure
*/
int handle_command(int sock, char *cmd, struct config_params *params)
{
    char table[MAX_TABLE_LEN] = "a", key[MAX_KEY_LEN+1] = "a", username[MAX_USERNAME_LEN+1] = "a", password[MAX_ENC_PASSWORD_LEN+1] = "a";
    struct storage_record stTemp;
    char value[sizeof stTemp.value];
    char* args[5];
	char data[100];
    // Get arguments into args and numArgs holds number of arguments given whic is 3 or 4.
    // @note: Need to free all args once done
    int numArgs = getArguments(args, cmd);
    // Note: Must free all args later
    int i = 0;
    sprintf(message, "Processing command '%s'\n", cmd);
    logger(fileServer, message);
    // @note: sock is the socket file descriptor (sockfd)
    // Send the command out to sockfd, which is the server
    // and end with a new line
// printf("arguments receive are:\n%s %s %s %s\n", args[0], args[1], args[2], args[3]);
    // Handle Commands
    //--------------------------------------------
    // GET table key (NULL)
    //--------------------------------------------
printf("HAHA902\n"); 
    if (strcmp(args[0], "GET") == 0)
    {
	if(findTableName(args[1]) == 0)
	{
            sprintf(message, "-2\n"); // 1=> success
            sendall(sock, message, strlen(message));
	    return 0; // server shouldnt crash if client fails
	}
	
	int tokenOut = 0;

	int getStat = getValue(args[1], args[2], data, &tokenOut); // get the value on args[3] 
	switch (getStat)
	{
	    case -1:
		sprintf(message, "-1\n");
		sendall(sock, message, strlen(message));
		return 0; // server shouldn't crash if client fails 
		break;
	    case 0:
		sprintf(message, "0\n");
		sendall(sock, message, strlen(message));
		return 0; // server shouldn't crash if client fails 
		break;
	    case 1:
		// Send success and actual message together
		sprintf(message, "1@%s@%d\n", data,tokenOut);
		sendall(sock, message, strlen(message));
		return 0;
		break;
	    default: // should never be here
		break;
	}
    }

    //--------------------------------------------
    // SET table key value
    //--------------------------------------------
    if (strcmp(args[0], "SET") == 0)
    {
	if(concurrency == 1)
		pthread_mutex_lock(&mymutex);		// Lock
	if(findTableName(args[1]) == 0)
	{
            sprintf(message, "-2\n"); // 1=> success
            sendall(sock, message, strlen(message));
	    if(concurrency == 1)
			pthread_mutex_unlock(&mymutex);		// If exitting unlock
	    return 0;	// Server should not crash regardless 
	}
	// If given NULL string as input, args 3 should be NULL
	if (strcmp(args[3], "NULL") == 0 || strcmp(args[3], "") == 0)
	{
	   args[3] = NULL;
	}
	int insertStat;
	int i = 0;
	char** colParam;
	char** colName;
	char** colType;
	int numCol;
	char* tempkey = "InitialKey";
	int token = 0;
	token = atoi(args[4]);
	getValue(args[1], tempkey, data, &i); // Get data in InitialKey which is entry containing config data

	numCol = getCol(data);	// Number of columns
	colParam = (char**) malloc(sizeof(char**) * numCol);	// Initialize an array to store each column name
	for(i = 0; i < numCol; i++)
	{
		colParam[i] = (char*) malloc(sizeof(char*) * (MAX_COLNAME_LEN+11));
	}
	colName = (char**) malloc(sizeof(char**) * numCol);	// Initialize an array to store each column name
	for(i = 0; i < numCol; i++)
	{
		colName[i] = (char*) malloc(sizeof(char*) * (MAX_COLNAME_LEN+1));
	}
	colType = (char**) malloc(sizeof(char**) * numCol);	// Initialize an array to store each column name
	for(i = 0; i < numCol; i++)
	{
		colType[i] = (char*) malloc(sizeof(char*) * 11);
	}
	getArgumentsJer(colParam, data);	// Get column data
	separateParam(colParam, colName, colType, numCol);	// Separate column data
	if(args[3]==NULL||args[3]=="NULL"){
		if(concurrency == 1 || concurrency == 2 )
			insertStat = insertValueTok(args[1], args[2], args[3],token);
		else
			insertStat = insertValue(args[1], args[2], args[3]);
	}
	else if(validParam(colName, args[3], colType, numCol))	//	Is it valid parameters?
	{
		if(concurrency == 1 || concurrency == 2)
			insertStat = insertValueTok(args[1], args[2], args[3],token);
		else
			insertStat = insertValue(args[1], args[2], args[3]);
		i = strlen(args[3]);
		if(i > 799)
			i = -3;		//	Value too long
	}
	else
		insertStat = -3;

	for(i = 0; i < numCol; i++)	//	Free colParam
	{
		free(colParam[i]);

	}
	free(colParam);
	colParam=NULL;
	for(i = 0; i < numCol; i++)	//	Free colName
	{
		free(colName[i]);
		colName[i]=NULL;
	}
	free(colName);
	colName=NULL;
	for(i = 0; i < numCol; i++)					//	Free colType
	{
		free(colType[i]);
		colType[i]=NULL;
	}
	free(colType);
	colType=NULL;
	switch (insertStat)
	{
	    case -5:
		sprintf(message, "-5\n");
		sendall(sock, message, strlen(message));
	   	if(concurrency == 1)
			pthread_mutex_unlock(&mymutex);		// If exitting unlock
		return 0; // Server shouldnt crash regardless		
	    case -3:
		sprintf(message, "-3\n");
		sendall(sock, message, strlen(message));
	   	if(concurrency == 1)
			pthread_mutex_unlock(&mymutex);		// If exitting unlock
		return 0; // Server shouldnt crash regardless
	    case -1:
		sprintf(message, "-1\n");
		sendall(sock, message, strlen(message));
	   	if(concurrency == 1)
		pthread_mutex_unlock(&mymutex);		// If exitting unlock
		return 0; // server shouldnt crash regardless
		break;
	    case 1:
		sprintf(message, "1\n");
		sendall(sock, message, strlen(message));
	   	if(concurrency == 1)
			pthread_mutex_unlock(&mymutex);		// If exitting unlock
		return 0;
		break;
	    case 2:
		sprintf(message, "2\n");
		sendall(sock, message, strlen(message));
	   	if(concurrency == 1)
			pthread_mutex_unlock(&mymutex);		// If exitting unlock
		return 0;
		break;
	    case 3:
		sprintf(message, "3\n");
		sendall(sock, message, strlen(message));
	   	if(concurrency == 1)
			pthread_mutex_unlock(&mymutex);		// If exitting unlock
		return 0;
		break;
	    default:
		break;
	}
    }
    //--------------------------------------------
    // AUTH username encrptpassword (NULL)
    //--------------------------------------------
    if (strcmp(args[0], "AUTH") == 0)
    {
	if(concurrency == 1)
	{
		pthread_mutex_lock(&mymutex);		// Lock
	}
	if (strcmp(args[1], params->username) == 0)
	{
	    if(strcmp(args[2], params->password) == 0)
	    {
		struct client* newclient;
//TODO: account for case where client not connected, make a check_connect() and check_auth() function
		newclient = getclient(sock);
		strcpy(newclient->username, params->username);
		strcpy(newclient->cryptpasswd, params->password);
		newclient->auth = true;
		sprintf(message, "1\n"); // 1=> success
		sendall(sock, message, strlen(message));
	   	if(concurrency == 1)
			pthread_mutex_unlock(&mymutex);		// If exitting unlock
		return 0;
	    }
	}
	sprintf(message, "0\n");
	sendall(sock, message, strlen(message));
	if(concurrency == 1)
	{
		pthread_mutex_unlock(&mymutex);		// If exitting unlock	
	}
	return 0;
    }
	//--------------------------------------------
    // QUERY table predicates max_keys
    //--------------------------------------------
    if (strcmp(args[0], "QUE") == 0)
    {
	
	if(findTableName(args[1]) == 0)
	{
            sprintf(message, "-2\n"); // 1=> success
            sendall(sock, message, strlen(message));
	    return 0; // server must not crash regardless
	}
	int i = 0;
	char** predName;
	char* oper;
	char** predValue;
	char** colParam;
	char** colName;
	char** colType;
	char** keyOut;
	int numCol;
	int max_keys;
	char* tempkey = "InitialKey";
	int noError=1;
	int numKeysOut=0;
	int totalRecords=0;

	
//--------------------------------------------------------------------
//This portion gets the config data from the data structure
	getValue(args[1], tempkey, data, &i); // Get data in InitialKey which is entry containing config data

	numCol = getCol(data);	// Number of columns
	
	colParam = (char**) malloc(sizeof(char**) * numCol);	// Initialize an array to store each column name
	for(i = 0; i < numCol; i++)
	{
		colParam[i] = (char*) malloc(sizeof(char*) * (MAX_COLNAME_LEN+11));
	}
	
	colName = (char**) malloc(sizeof(char**) * numCol);	// Initialize an array to store each column name
	for(i = 0; i < numCol; i++)
	{
		colName[i] = (char*) malloc(sizeof(char*) * (MAX_COLNAME_LEN+1));
	}
	
	colType = (char**) malloc(sizeof(char**) * numCol);	// Initialize an array to store each column name
	for(i = 0; i < numCol; i++)
	{
		colType[i] = (char*) malloc(sizeof(char*) * 11);
	}
	
	getArgumentsJer(colParam, data);	// Get column data
	
	separateParam(colParam, colName, colType, numCol);	// Separate column data
	
//-----------------------------------------------------------------------------
//This for initializing and parsing from predicate string
	
	predName = (char**) malloc(sizeof(char**) * MAX_COLUMNS_PER_TABLE);	// Initialize an array to store each column name
	for(i = 0; i < MAX_COLUMNS_PER_TABLE; i++)
	{
		predName[i] = (char*) malloc(sizeof(char*) * (MAX_COLNAME_LEN+1));
	}
	
	oper = (char*) malloc(sizeof(char*) * (MAX_COLUMNS_PER_TABLE+1));	// Initialize an array to store each column name
	
	predValue = (char**) malloc(sizeof(char**) * MAX_COLUMNS_PER_TABLE);	// Initialize an array to store each column name
	for(i = 0; i < 10; i++)
	{
		predValue[i] = (char*) malloc(sizeof(char*) * (MAX_VALUE_LEN+1));
	}

	max_keys = atoi(args[3]);
	keyOut = (char**) malloc(sizeof(char**) * max_keys);	// Initialize an array to store each column name
	for(i = 0; i < max_keys; i++)
	{
		keyOut[i] = (char*) malloc(sizeof(char*) * (MAX_COLNAME_LEN+1));
	}
	
	if(args[2]!="NULL")
	{
	noError = getPred(args[2], predName, oper, predValue);
	noError = checkPredDuplicate(predName, oper);
	
	if(noError==1)
	{
		noError = validPredName(colName, colType, numCol, predName, predValue, oper);
	}
	
	
	if(noError)
	{
		totalRecords = findKeys(predName, predValue, oper, args[1], keyOut, max_keys, numCol);
		numKeysOut = findKeysCopiedNum(predName, predValue, oper, args[1], keyOut, max_keys, numCol);
	}
	}
	else
	{
		noError = 1;
		totalRecords = getAllKeys(args[1], keyOut, max_keys);
		numKeysOut = getAllKeysCopiedNum(args[1], keyOut, max_keys);
	}
	
//-----------------------------------------------------------------------
//This section deals with freeing memory	
	for(i = 0; i < MAX_COLUMNS_PER_TABLE; i++)	//	Free predName
	{
		free(predName[i]);

	}
	free(predName);
	predName=NULL;
		
	free(oper);
	oper=NULL;
	
	for(i = 0; i < MAX_COLUMNS_PER_TABLE; i++)					//	Free predValue
	{
		free(predValue[i]);
		predValue[i]=NULL;
	}
	free(predValue);
	predValue=NULL;
	
	for(i = 0; i < numCol; i++)	//	Free colParam
	{
		free(colParam[i]);

	}
	free(colParam);
	colParam=NULL;			
		
	for(i = 0; i < numCol; i++)	//	Free colName
	{
		free(colName[i]);
		colName[i]=NULL;
	}
	free(colName);
	colName=NULL;
	
	for(i = 0; i < numCol; i++)					//	Free colType
	{
		free(colType[i]);
		colType[i]=NULL;
	}
	free(colType);
	colType=NULL;
//----------------------------------------------------------------------------
	
	switch (noError)
	{
	    case 0:
		sprintf(message, "-3\n");
		sendall(sock, message, strlen(message));
		return 0; // server must not exit due to error from client
		break;
	    case 1:
		// Send success and actual message together
		sprintf(message, "%d", totalRecords);
		for(i = 0; i < numKeysOut; i++)
		{
			strcat(message, "@");
			strcat(message, keyOut[i]);
		}
		strcat(message, "\n");
		
		for(i = 0; i < max_keys; i++)	//	Free colName
		{
			free(keyOut[i]);
		}
		free(keyOut);
		sendall(sock, message, strlen(message));
		return 0;
		break;
	    default: // should never be here
		break;
	}
    }

    // @note free(args[i]) here will crash, not sure if need to free
    return 0;
}



void serverThreads(void* thread_data){
	struct thread_data* mydata;
	mydata = (struct thread_data*) thread_data;
	struct config_params* params;
	params = mydata->params;
	int clientsock = mydata->clientsock;
	struct sockaddr_in clientaddr = mydata->clientaddr;
	int* threadNum = mydata->threadNum;
	int wait_for_commands = 1;
	int status = 0;
	srand(time(NULL));	// Seed for counter intializations
	do
	{
	    // Read a line from the client.
	    char cmd[MAX_CMD_LEN];
	    // Read from clientsock's file descriptor to command
	    int status = recvline(clientsock, cmd, MAX_CMD_LEN);
	    if (status != 0)
	    {
		// Here, either an error occurred or the client closed the connection.
		// Stop waiting for commands
		wait_for_commands = 0;
	    }
	    else
	    {
		// Handle the command from the client.
		// @note: For milestone1, this just prints it back out to server
		//********** TESTING ONLY
		//**********/

		int status = handle_command(clientsock, cmd, params);
		// If handled command fails, stop waiting for command
		if (status != 0)
		{
		    printf("Error: Handle command return status of -1 to main() in server");
		    wait_for_commands = 0; // stop waiting for commands
		    exit(EXIT_FAILURE); // @note original code for M1 by prof. was to just EXIT_SUCCESS at bottom
		}
            }
         }
	while (wait_for_commands); //do while client is still interacting
        // Close the connection with the client.
//	close(clientsock);
	sprintf(message, "Closed connection from %s:%d.\n", inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port);
	logger(fileServer, message);
	(*threadNum)--;
	pthread_exit(NULL);
    }





// This function reads and process one client p to see what commands or messages it has to say
static void read_process(struct client *p1)
{

}


// This function reads a line from a client's buffer
// and returns a char pointer to the first character of the line
// or NULL if the line is not fully filled
char *myreadline(struct client *p)
{

}


// Given the head of a circular linked list of cards,
// print each card's value on the same line separated by spaces.
// Print a newline after printing all card's values.
void printCardInfo (Card *cardlist)
{
    // if cardlist is empty
    // do nothing.
    if (!cardlist)
        return;
    Card *head = cardlist;
    do
    // print the values of each card
    // while the cardlist is not back to
    // its beginning
    {
        printf ("%d ", cardlist->val);
        cardlist = cardlist->next;
    } while (cardlist != head);
      // prints a newline once all the values
      // have been printed out.
    printf ("\n");
}

// Create a new circular linked list of cards
// with one card of value val. Both the head and
// that one card with value val points to each other.
// Return the pointer to the head.
Card *newCard (int val)
{
    // Initialize two pointers.
    // one as a head pointer
    // whereas the other as a pointer to the
    // first card in the circular linked list
    Card *cardlist = NULL;
    Card *newCard = malloc(sizeof(Card));
    if (newCard == NULL)
    {
        perror("newCard failed to malloc");
        exit(EXIT_FAILURE);
    }
    // initialize the value of the newCard to val
    newCard->val = val;
    // make both pointers point to each other
    newCard->next = newCard;
    cardlist = newCard;
    // return pointer to the head
    // of the new circular linked list.
    return cardlist;
}

// Given a pointer to the head of a circular linked list,
// and the value of a new card,
// add the new card to the END of cardlist.
// and returns the head of the circular linked list.
Card *addTailCard (Card *cardlist, int val)
{
    Card *curr = NULL;
    curr = cardlist;
    while (curr->next != cardlist)
    {
        curr = curr->next;
    }
    // now curr is pointing to the last card.
    // and curr->next is pointing to the first card
    // initialize newCard with given value.
    Card *newCard = malloc(sizeof(Card));
    if (newCard == NULL)
    {
        perror("addTailCard failed to malloc");
        exit(EXIT_FAILURE);
    }
    newCard->val = val;
    // make newCard point to first Card
    newCard->next = curr->next;
    // make previous card point to
    // current last card.
    curr->next = newCard;
    // return head that still points to first card.
    return cardlist;
}

// A helper function (not to be used outside card.c)
// Given the head of a circular linked list of cards,
// and the value of a card, return the card prior to that card.
// The next pointer of the returned card is card with value val.
// If val is not found, return NULL.
Card *findCard (Card *cardlist, int val)
{
    Card *prev = NULL;
    Card *curr = cardlist;
    // if value is at first card
     if (curr->val == val)
     {
         while (curr->next != cardlist)
         {
             curr = curr->next;
         }
         // here, curr is pointing to the last card
         return curr;
     }
    // while the value is not found
    // and it has not looped through the whole deck.
    while( curr->val != val && curr->next != cardlist )
    {
        // let prev be the card before curr.
        prev = curr;
        // curr moves on to the next card in the
        // circular linked list deck.
        curr = curr->next;
    }
    // Here, either the value is found
    // or we are at the last card of the deck.
    // Check if the current card
    // or the last card is the value val
    // if it is, return prev.
    if (curr->val == val) return prev;
    // if it is not, the value is not found.
    return NULL;
}

// Given the head of a circular linked list of cards,
// and the value of an existing card,
// swap that card with the next card in cardlist.
// and return the head to the circular linked list of cards
Card *swapAfter (Card *cardlist, int val)
{
    Card *curr = cardlist;
    // initialize prev just in case
    // value is in the first card.
    // then prev will be pointing
    // to the last card.
    Card *prev = findCard(cardlist, cardlist->val);
    while ( curr->val != val)
    {
        prev = curr;
        curr = curr->next;
    }
    // if the last card swaps the top card instead.
    if (curr->next == cardlist)
    {
        // return the header that points to the new top card
        cardlist = curr;
    }
    // if the top card swaps with the 2nd card
    else if (prev->next == cardlist)
    {
        // return cardlist pointing to the new first card.
        cardlist = curr->next;
    }
    prev->next = curr->next;
    curr->next = curr->next->next;
    prev->next->next = curr;
    return cardlist;
}

// Given the head of a circular linked list of cards,
// and the value of an existing card,
// return the position of that card
// in the circular linked list.
// Top card => position 1
// Bottom card => position 28
int cardPos (Card *cardlist, int val)
{
    // variable to keep track of
    // position of card
    int pos = 1;
    Card *curr = cardlist;
    while( curr->val != val)
    {
        curr = curr->next;
        pos++;
    }
    return pos;
}

// Given the head of a circular linked list of cards
// and the values of the top and bottom jokers (27 or 28)
// perform a triple cut.
// Triple Cut = swap cards above the top joker,
// with cards below the second joker.
Card *tripleCut (Card *cardlist, int top, int bot)
{
    // Initialize pointers to point to
    // the first and last card above the top joker
    // and first and last card below the bottom joker.
    Card *begin_top = NULL, *end_top = NULL;
    Card *begin_bot = NULL, *end_bot = NULL;
    // a pointer to the bottom joker.
    Card *joker_bot = NULL;

    // begin_top points to the first card.
    begin_top = cardlist;
    // end_top points to the card before the top joker.
    end_top = findCard(cardlist, top);
    // joker_bot points to the bottom joker.
    joker_bot = findCard(cardlist, bot);
    joker_bot = joker_bot->next;
    // begin_bot points to the card after the bottom joker.
    begin_bot = joker_bot->next;
    //end_bot points to the last card.
    end_bot = findCard(cardlist, cardlist->val);

    // initialize variables to get the positions of both jokers
    // for case handling.
    int top_pos = cardPos(cardlist, top);
    int bot_pos = cardPos(cardlist, bot);

    // Case 1: top joker is first card and bottom joker is last card
    if (top_pos == 1 && bot_pos == 97)
    {
        // do nothing
        return cardlist;
    }

    // Case 2 : top joker is the first card.
    else if (top_pos == 1)
    {
        // change the position of the first card
        cardlist = begin_bot;
        return cardlist;
    }
    // Case 3: bottom joker is the last card.
    else if (bot_pos == 97)
    {
        // make top joker the first card.
        cardlist = end_top->next;
        return cardlist;
    }
    // The ELSE statement covers:
    // Case 4: top joker is the 2nd card
    // Case 5: bottom joker is the 2nd last card
    // Case 6: both jokers are not on the top 2 or bottom 2 positions on the deck.
    else
    {
        // update cardlist to the new top card which is begin_bot
        cardlist = begin_bot;
        // point the last card to the top joker.
        end_bot->next = end_top->next;
        // point the end_top which will be the last card
        // to the new top_card.
        end_top->next = cardlist;
        joker_bot->next = begin_top;
    }
    return cardlist;
}

// Given the head of a circular linked list of cards,
// and a position, return value of card
// at that position. Top card => position 1
int cardVal(Card *cardlist, int pos)
{
    int count = 1;
    Card *curr = cardlist;
    while(count != pos)
    {
        count++;
        curr = curr->next;
    }
    return curr->val;
}

// Given the head of a circular linked list of cards,
// insert num top cards above the bottom-most card
Card *insertBottom (Card *cardlist, int num)
{
    // special case where bottom card number is 28.
    if (num == 97)
    {
        num = 96;
    }
    // initialize pointers to bottom card
    // and the card before bottom card.
    Card *bot_card = findCard(cardlist, cardlist->val);
    Card *bef_bot = findCard (cardlist, bot_card->val);

    // initialize pointer to the card at position num.
    int num_value = cardVal(cardlist, num);
    Card *num_card = findCard(cardlist, num_value);
    num_card = num_card->next;

    // if we need to move 27 cards
    if (num == 96)
    {
        // do nothing.
        return cardlist;
    }
    // else
    // move num top cards above bottom-most card
    bef_bot->next = cardlist;
    bot_card->next = num_card->next;
    num_card->next = bot_card;
    cardlist = bot_card->next;
    return cardlist; // remember to make sure cardlist points to the top card
}

// Free all memory for all cards in
// the circular linked list of cards.
void deallocateCards (Card *cardlist)
{
    // if cardlist is empty, do nothing.
    if (cardlist == NULL) return;
    Card *prev;
    Card *curr = cardlist;
    // if cardlist has next..
    if(cardlist->next)
    { curr = curr->next; // make curr point to cardlist->next
     while(curr->next != cardlist)
     {
         prev = curr;
         curr = curr->next;
         free(prev); // might free nothing sometimes
     }
     free(curr->next); // free cardlist
free (curr); // free current.
    }
    else { free (curr); } // else just free cardlist.
}



// This function returns the first pointer to the next occurence of /r/n, \r, or \n
// and NULL if it is not found within the limit
char* extractline(char *s, int limit)
{
}

// This function takes in a pointer to a file,
// which carries information about a deck.
// It reads all cards from the deck file and
// returns them as a circular linked list.
Card *readDeck (char *filename)
{
    FILE *fp;
    fp = fopen(filename, "r");
    if (fp == NULL)
    {
        perror("Can't open file in readDeck\n");
        exit(EXIT_FAILURE);
    }
    int val = 0; // initializing val
    int temp = 0; // to know when to stop scanning
    fscanf(fp, "%d", &val);
    Card *cardlist = newCard(val);
    while ( fscanf(fp, "%d", &val) && val != temp )
    {
        temp = val;
        cardlist = addTailCard(cardlist, val);
    }
    //close the file
    fclose(fp);
    return cardlist;
}

// Read and store a line from message fp into str.
// Store at most n characters.
// Discard non-letters and
// convert all letters to uppercase.
// Str will always be a valid string.
// and returns the number of new characters stored.
int readLine(FILE *fp, char str[], int n)
{
    if (fp == NULL)
    {
        perror("Can't open file in readLine\n");
        exit(EXIT_FAILURE);
    }
    // clear the str given to start storing from index 0.
    strcpy(str,""); // make str an empty str
    int count = 0; // to return number of characters stored.
    char c = ' ';
    char temp[2];
    while ( c != '\n' && count < n)
    {
        count++;
        fscanf(fp, "%c", &c);
        // isalpha returns true if character read is a letter (doesn't include spaces)
        if (isalpha(c))
        {
            temp[0] = c;
            temp[1] = '\0';
            strcat(str, temp);
        }
    }
    // convert all letters to uppercase
    int i = 0;
    for ( ; str[i] != '\0'; i++)
    {
        str[i] = toupper(str[i]);
    }
    // get length of string to return.
    if (strcmp(str, "") != 0) count = strlen(str);
    else count = 0;
    return count;
}

// Given the head of a circular linked list of cards,
// generate the next keystream value, (one round of solitaire)
// and store it in the memory pointed to by bit.
Card *nextRound (Card *deck, int *bit)
{
    // 1. Swap 27 once
    deck = swapAfter(deck, 96);
    // 2. Swap 28 twice.
    deck = swapAfter(deck, 97);
    deck = swapAfter(deck, 97);
    // 3. Perform Triple Cut
    // determine which Joker is higher
    // by comparing their positions
    int Joker1 = cardPos(deck, 96);
    int Joker2 = cardPos(deck, 97);
    // if 28 is above 27 in the deck
    if (Joker1 > Joker2)
    {
        deck = tripleCut(deck, 97, 96);
    }
    // if 27 is above 28 in the deck.
    else // if Joker2 > Joker 1
    {
        deck = tripleCut(deck, 96, 97);
    }
    // 4. Insert cards according to value of bottom
    // card of deck.
    int val = cardVal(deck, 97);
    deck = insertBottom(deck, val);
    // 5. Get the keystream value
    // Count down to the deck according
    // to the value of the top card
    int count = cardVal(deck, 1);
    if (count == 97) count = 96;
    // get the value of the count+1 card
    val = cardVal(deck, count + 1);
    // if the value is a joker
    if (val == 96 || val == 97)
    { // redo round from step 1 using recursion
        deck = nextRound(deck, bit);
    }
    // use else statement to prevent recursion
    // from overriding value of bit.
    else { *bit = val;}
    // return the deck for next round of solitaire.
    return deck;
}

// Given a deck filename, a message filename, and a mode
// ('e' or 'd'), encrypt or decrypt the messages using the deck.
// Output the processed messages on standard output, one per line.

// ONLY WORKS IF EVERYTHING IS CAPS 
process (char *deckfname, char *msg, char * newmsg,  char mode)
{
    Card *cardlist;
    cardlist = readDeck(deckfname);
    if (cardlist == NULL)
    {
        printf("Can't read deck file\n");
        exit(EXIT_FAILURE);
    }
    // initialize i to get number of chars read onto str.
    int i = 1;

    // Variables for generating keystream values
    int *bit = malloc(sizeof(int));
    *bit = 0;
    int j = 0; // to index the string
    int num = 0; // to represent digit values of characters.
    // if encryption mode.
printf("Message received intofunc is %s\n", msg); 
// printf("Message is to encrpyt/decrypt is %s\n" , msg); 
    if (mode == 'e')
    { // encrypt message
        // loop until whole file is read line by line
            for ( j=0 ; msg[j] != '\0' ; j++)
            {
                num = msg[j] - ' ' + 1; // change into number representation.
//              printf("the value of j in letter is %c and in number is %d\n", msg[j], num);
                cardlist = nextRound (cardlist, bit);
  //             printf("bit is value %d\n", *bit);
                num += *bit; if (num > 95) num -= 95;
                num += (' ' - 1); // convert back to proper letter.
  //              printf("%c", num);
		newmsg[j] = num; 
            }
            printf("\n"); // print new line.
printf("new message is %s\n", newmsg); 
printf("Encrypt: Done changing number representation\n"); 
            printf("\n"); // print new line

    }
    else if (mode == 'd')
    { // decrypt message
	printf(" IN DECRYPT MODE IN FUNC\n"); 
        // loop until whole file is read line by line
            for ( j=0 ; msg[j] != '\0' ; j++)
            {
                num = msg[j] - ' ' + 1; // change into number representation.
              // printf("the value of j in letter is %c and in number is %d\n", str[j], num);
                cardlist = nextRound (cardlist, bit);
          //     printf("bit is value %d\n", *bit);
                if (num <= *bit) num += 95;
                num -= *bit;
                num += (' ' - 1); // convert back to proper letter.
    //            printf("%c\n", num);
		newmsg[j] = num; 
            }
            printf("\n"); // print new line.
printf("new message is %s\n", newmsg); 
printf("Decrypt: Done changing number representation\n"); 
    }
    // free the memory allocated
    deallocateCards(cardlist);
    free(bit);
    return newmsg; 
}

